package cn.ycc1.functionlibrary.streams;

/**
 * Creating Your Own Collector
 * @author ycc
 * @date 2025/3/8
 */
public class OwnCollector {
    /**
     * Understanding How a Collector Works
     * As we mentioned earlier, the Collectors factory class only addresses streams of objects because the collect() method
     * that takes a collector object as an argument only exists in Stream. If you need to collect a stream of numbers,
     * then you need to understand what are the building elements of a collector.
     *
     * In a nutshell, a collector is built on four basic components. The first two are used to collect the elements of the
     * stream. The third one is only needed for parallel streams. The fourth one is needed for certain types of collectors,
     * which need a post-processing on the built container.
     *
     * The first component is used to create the container in which the elements of the stream will be collected. This container
     * is easy to identify. For instance, in the cases we covered in the previous part, we used the ArrayList class,
     * the HashSet class, or the HashMap class. Creating such a container can be modeled with an instance of Supplier.
     * This first component is called the supplier.
     *
     * The second component models the adding of a single element from the stream to this container. This operation will be
     * called repeatedly by the implementation of the Stream API, to add all the elements of the stream one by one to the
     * container.
     *
     * In the Collector API, this component is modeled by an instance of BiConsumer. This biconsumer takes two arguments.
     *
     * The first one is the container itself, partially filled with the previous elements of the stream.
     * The second one is the element of the stream that should be added to this partially filled container.
     * This biconsumer is called the accumulator in the context of the Collector API.
     *
     * These two components should be sufficient for a collector to work, but the Stream API brings a constraint that makes two
     * more components necessary for a collector to work properly.
     *
     * You may remember that the Stream API supports parallelization. This point will be covered in more detail later in this
     * tutorial. What you need to know is that parallelization splits the elements of your stream in substreams, each one being
     * processed by a core of your CPU. The Collector API can work in such a context: each substream will just be collected in
     * its own instance of the container created by your collector.
     *
     * Once these substreams have been processed, you have several containers each containing the elements from the sub-stream
     * it processed. These containers are identical, because they have been created with the same supplier. Now, you need a way
     * to merge them into one. To be able to do that, the Collector API needs a third component, a combiner, that will merge
     * these containers together. A combiner is modeled by an instance of BinaryOperator that takes two partially filled
     * containers and returns one.
     *
     * This BinaryOperator is also modeled by a BiConsumer in the collect() overloads of the Stream API.
     *
     * The fourth component is called the finisher, and will be covered later in this part.
     */

    /**
     * Collecting Primitive Types in a Collection
     * With the first three components, you can use the collect() method from the specialized streams of numbers.
     * The IntStream.collect() method takes three arguments:
     *
     * an instance of Supplier, called supplier;
     * an instance of ObjIntConsumer, called accumulator;
     * an instanceof BiConsumer, called combiner.
     */

    /**
     * Collecting Primitive Types in a StringBuffer
     * Let us examine how you can implement an equivalent of the Collectors.joining() to join the elements of a stream of
     * primitive types in a single string of characters. The String class is immutable, so there is no way you can accumulate
     * elements in it. Instead of using the String class, you can use the StringBuffer class, which is mutable.
     */

    /**
     * Using a Finisher to Post-Process a Collector
     * The code you wrote in the previous paragraph is almost doing what you need: it joins strings of characters in an instance
     * of StringBuffer, from which you can create a regular String object by just calling its toString() method. But the
     * Collectors.joining() collector produces a String directly without having you to call toString(). So how is it done?
     *
     * The Collector API defines a fourth component precisely to handle this case, which is called a finisher. A finisher is an
     * instance of Function that takes the container in which the elements were accumulated and transforms it to something else.
     * In the case of the Collectors.joining(), this function is just the following.
     */

    /**
     * Combining the Results of Two Collectors with the Teeing Collector
     * A method was added in the Collectors class in Java SE 12 called teeing(). This method takes two downstream collectors and
     * a merging function.
     *
     * Let us go through a use case to see what you can do with collector. Imagine you have the following Car and Truck record.
     */
}
